package com.hsogoo.ultraman.spring.refreshable;

import com.hsogoo.ultraman.container.ConfigContainer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.stereotype.Service;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * @Author huangsaigang
 * Created @2018/9/23.
 */
@Slf4j
@Service
public class RefreshableConfigProcessor implements BeanPostProcessor, BeanFactoryPostProcessor, PriorityOrdered {

    private final RefreshableConfigRegistry refreshableConfigRegistry;
    private final PlaceholderHelper placeholderHelper;

    public RefreshableConfigProcessor() {
        this.refreshableConfigRegistry = ConfigContainer.getBean(RefreshableConfigRegistry.class);
        this.placeholderHelper = ConfigContainer.getBean(PlaceholderHelper.class);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        
    }

    //after postProcessBeanFactory
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class clazz = bean.getClass();
        for (Field field : findAllField(clazz)) {
            processField(bean, beanName, field);
//            processBeanPropertyValues(bean, beanName);
        }
        return bean;
    }
//
//    private void processBeanPropertyValues(Object bean, String beanName) {
//        Collection<RefreshConfigDefinition> propertyRefreshConfigs = beanName2RefreshConfigDefinitions
//                .get(beanName);
//        if (CollectionUtils.isEmpty(propertyRefreshConfigs)) {
//            return;
//        }
//
//        for (RefreshConfigDefinition definition : propertyRefreshConfigs) {
//            try {
//                PropertyDescriptor pd = BeanUtils
//                        .getPropertyDescriptor(bean.getClass(), definition.getPropertyName());
//                Method method = pd.getWriteMethod();
//                if (method == null) {
//                    continue;
//                }
//                RefreshableConfig RefreshConfig = new RefreshableConfig(definition.getKey(), definition.getPlaceholder(),
//                        bean, beanName, );
//                RefreshConfigRegistry.register(definition.getKey(), RefreshConfig);
//                logger.debug("Monitoring {}", RefreshConfig);
//            } catch (Throwable ex) {
//                logger.error("Failed to enable auto update feature for {}.{}", bean.getClass(),
//                        definition.getPropertyName());
//            }
//        }
//    }

    private void processField(Object bean, String beanName, Field field) {
        Value value = field.getAnnotation(Value.class);
        if (value == null) {
            return;
        }
        //抽取出value的placeholder的配置项
        Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());

        if (keys.isEmpty()) {
            return;
        }

        //放入到refreshableConfigRegistry中
        for (String key : keys) {
            RefreshableConfig refreshableConfig = new RefreshableConfig(key, value.value(), bean, beanName, field);
            refreshableConfigRegistry.register(key, refreshableConfig);
        }
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        //make it as late as possible
        return Ordered.LOWEST_PRECEDENCE;
    }

    private List<Field> findAllField(Class clazz) {
        final List<Field> res = new LinkedList<>();
        ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                res.add(field);
            }
        });
        return res;
    }
}
